package org.springframework.boot.autoconfigure.netty;


import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.netty.channelInitializer.AbstractChannelInitializer;
import org.springframework.boot.autoconfigure.netty.server.NettySocketServer;
import org.springframework.boot.autoconfigure.netty.support.RegisterBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.netty.annotation.HttpEndPoint;
import org.springframework.boot.netty.annotation.MethodArgumentResolver;
import org.springframework.boot.netty.argumentResolver.*;
import org.springframework.boot.netty.converter.ProtobufMessage2JsonConverter;
import org.springframework.boot.netty.listener.HttpEndPontPostProcess;
import org.springframework.boot.netty.service.DefaultUserDetailService;
import org.springframework.boot.netty.service.UserDetailService;
import org.springframework.boot.netty.converter.JsonMessageConverter;
import org.springframework.boot.netty.converter.MessageConverter;
import org.springframework.boot.netty.handler.factory.HearBeatHandlerFactory;
import org.springframework.boot.netty.handler.factory.NettyCustomerizerChannelInboundHandlerAdapterFactory;
import org.springframework.boot.netty.view.FreeMarkerView;
import org.springframework.boot.netty.view.ThymeleafView;
import org.springframework.boot.netty.view.View;
import org.springframework.boot.netty.viewResolver.FreeMarkerViewResolver;
import org.springframework.boot.netty.viewResolver.ThymeleafViewResolver;
import org.springframework.boot.netty.viewResolver.ViewResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.FileNotFoundException;

/**
 * Author: huoxingzhi
 * Date: 2020/12/10
 * Email: hxz_798561819@163.com
 */
@Configuration
@ConditionalOnClass({NioEventLoopGroup.class, NioEventLoop.class})
@EnableConfigurationProperties(NettyProperties.class)
public class NettyAutoConfiguration implements ApplicationContextAware, InitializingBean {

    @Autowired
    NettyProperties nettyProperties;

    BeanDefinitionRegistry beanDefinitonRegistry;

    @Bean
    NettySocketServer webSocketServer(NettyProperties nettyProperties){

        return new NettySocketServer(nettyProperties);
    }

   @Bean
   NettyStartupListener nettyStartupListener(NettySocketServer nettySocketServer, AbstractChannelInitializer channelInitializer){

       return new NettyStartupListener(nettySocketServer,channelInitializer);
   }

    @Bean
    @ConditionalOnProperty(prefix = "spring.netty",name = "start-up-mode",havingValue = "http")
    HttpEndPontPostProcess HttpEndPontPostProcess(){
        return new HttpEndPontPostProcess();
    }

   //============================================消息转换器配置======================================
    @Bean("jsonMessageConverter")
    MessageConverter jsonMessageConverter(){

        return  new JsonMessageConverter();
    }

    @Bean("protoBufMessageConverter")
    MessageConverter protoBufMessageConverter(){

        return  new ProtobufMessage2JsonConverter();
    }

   //============================================心跳handler配置======================================
    @Bean
    NettyCustomerizerChannelInboundHandlerAdapterFactory heartBeatHandler (){

        return new HearBeatHandlerFactory();
    }

    //============================================参数解析器配置======================================
    @Bean
    HandlerMethodArgumentResolver channelAwareMethodArgumentResolver(){

        return new ChannelAwareMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver nettyPayloadMethodProcessor(){

        return new NettyPayloadMethodProcessor();
    }


    @Bean
    HandlerMethodArgumentResolver errorMessageMethodArgumentResolver(){

        return new ErrorMessageMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver stringMethodArgumentResolver(){

        return new StringMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver beanAwareMethodArgumentResolver(){

        return new BeanAwareMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver requestParamMethodArgumentResolver(){
        return new RequestParamMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver pathVariableMethodArgumentResolver(){
        return new PathVariableMethodArgumentResolver();
    }

    @Bean
    HandlerMethodArgumentResolver requestEntityMethodArgumentResolver(){
        return new RequestEntityMethodArgumentResolver();
    }

    //==========================================返回值解析器配置======================================
    @Bean
    ReturnValueArgumentResolver modelAndViewReturnValueArgumentResolver(){
        return new ModelAndViewReturnValueArgumentResolver();
    }

    @Bean
    ReturnValueArgumentResolver channelAutoProcessMethodArgumentResolver(){
        return new ChannelAutoProcessMethodArgumentResolver();
    }

    @Bean
    ReturnValueArgumentResolver responseBodyReturnValueArgumentResolver(){
        return new ResponseBodyReturnValueArgumentResolver();
    }


   //======================================view以及ViewResolve===================================
    @ConditionalOnClass(freemarker.template.Template.class)
    @Bean
    ViewResolver freeMarkerViewResolver(){
        return new FreeMarkerViewResolver();
    }

    @ConditionalOnClass(org.thymeleaf.TemplateEngine.class)
    @Bean
    ViewResolver thymeleafViewResolver(){
        return new ThymeleafViewResolver();
    }

    @ConditionalOnClass(freemarker.template.Template.class)
    @Bean
    View freeMarkerView(NettyProperties nettyProperties) throws FileNotFoundException {
        return new FreeMarkerView(nettyProperties);
    }

    @ConditionalOnClass(org.thymeleaf.TemplateEngine.class)
    @Bean
    View thymeleafViewResolver(NettyProperties nettyProperties){
        return new ThymeleafView(nettyProperties);
    }

    //========================================默认的UserDetailService==================================
    @Bean
    @ConditionalOnMissingBean(UserDetailService.class)
    UserDetailService defaultUserDetailService(){

        return new DefaultUserDetailService();
    }

    //======================================ThrowableResolver===================================
    @Bean
    ThrowableResolver authorizationThrowableResolver(){

        return new AuthorizationThrowableResolver();
    }

    @Bean
    ThrowableResolver runtimeThrowableResolver(){

        return new RuntimeThrowableResolver();
    }

   //===================================注册额外的参数解析器以及http端点================================
    @Override
    public void afterPropertiesSet() throws Exception {
        RegisterBean.doRegister(this.beanDefinitonRegistry,this.nettyProperties.getArguementResolverBasePackages(), MethodArgumentResolver.class);

        RegisterBean.doRegister(this.beanDefinitonRegistry,this.nettyProperties.getHttpEndPointBasePackages(), HttpEndPoint.class);

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        this.beanDefinitonRegistry = (BeanDefinitionRegistry) configurableApplicationContext
                .getBeanFactory();
    }
}
